//
//  MM_CodeScanVC.m
//  MallManage_IOS
//
//  Created by 张浩 on 2023/10/6.
//

#import "MM_CodeScanVC.h"
#import <AVFoundation/AVFoundation.h>
#import "MM_CodeResultListVC.h"
#import "MM_AlertView.h"

@interface MM_CodeScanVC ()<AVCaptureMetadataOutputObjectsDelegate>

@property (nonatomic, strong) AVCaptureDevice *device;
@property (nonatomic, strong) AVCaptureDeviceInput *input;
@property (nonatomic, strong) AVCaptureMetadataOutput *output;
@property (nonatomic, strong) AVCaptureSession *session;
@property (nonatomic, strong) AVCaptureVideoPreviewLayer *preview;
@property (nonatomic, strong) NSMutableArray *qrCodesArray;
//多个二维码位置点击按钮数组  
@property (strong, nonatomic) NSMutableArray *qrCodesButtonArray;

@end

@implementation MM_CodeScanVC

- (void)viewDidLoad {
    [super viewDidLoad];
    self.titleView.title = @"扫描";
    
    AVAuthorizationStatus status = [AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo];
    NSString *tips = NSLocalizedString(@"AVAuthorization", @"您没有权限访问相机");
    if(status == AVAuthorizationStatusAuthorized) {
        
        // Device,这里需要注意的是AVCaptureDevice不能直接创建的实例
        self.device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
        
        //.初始化输入流 Input,并添加Device
        self.input = [AVCaptureDeviceInput deviceInputWithDevice:self.device error:nil];
        
        //.初始化输出流Output
        self.output = [[AVCaptureMetadataOutput alloc] init];

        // 创建view,通过layer层进行设置边框宽度和颜色,用来辅助展示扫描的区域
    //    UIView *redView = [[UIView alloc] initWithFrame:CGRectMake(100, 100, 200, 100)];
    //    redView.layer.borderWidth = 2;
    //    redView.layer.borderColor = [UIColor cyanColor].CGColor;
    //    [self.view addSubview:redView];
        
        //设置输出流的相关属性
        // 确定输出流的代理和所在的线程,这里代理遵循的就是上面我们在准备工作中提到的第一个代理,至于线程的选择,建议选在主线程,这样方便当前页面对数据的捕获.
        [self.output setMetadataObjectsDelegate:self queue:dispatch_get_main_queue()];
        
    //    self.output.rectOfInterest = CGRectMake((100)/(ScreenH),(ScreenW - 100 - 200)/ScreenW,100/ScreenH,200/ScreenW);
        
        // 初始化session
        self.session = [[AVCaptureSession alloc]init];
        // 设置session类型,AVCaptureSessionPresetHigh 是 sessionPreset 的默认值。
        [_session setSessionPreset:AVCaptureSessionPresetHigh];
        
        // 添加输入流
        if ([_session canAddInput:self.input]) {
            [_session addInput:self.input];
        }
        // 添加输出流
        if ([_session canAddOutput:self.output]) {
            [_session addOutput:self.output];
        }
        
        // 下面的是比较重要的,也是最容易出现崩溃的原因,就是我们的输出流的类型
        // 1.这里可以设置多种输出类型,这里必须要保证session层包括输出流
        //   2.必须要当前项目访问相机权限必须通过,所以最好在程序进入当前页面的时候进行一次权限访问的判断(在文章的最后,我会贴出相关的代买)
        self.output.metadataObjectTypes =@[
                                       AVMetadataObjectTypeUPCECode,
                                       AVMetadataObjectTypeCode39Code,
                                       AVMetadataObjectTypeCode39Mod43Code,
                                       AVMetadataObjectTypeEAN13Code,
                                       AVMetadataObjectTypeEAN8Code,
                                       AVMetadataObjectTypeCode93Code,
                                       AVMetadataObjectTypeCode128Code,
                                       AVMetadataObjectTypePDF417Code,
                                       AVMetadataObjectTypeQRCode,
                                       AVMetadataObjectTypeAztecCode,
                                       AVMetadataObjectTypeInterleaved2of5Code,
                                       AVMetadataObjectTypeITF14Code,
                                       AVMetadataObjectTypeDataMatrixCode,
                                       ];
        // 初始化
        self.preview =[AVCaptureVideoPreviewLayer layerWithSession:_session];
        // 设置Video Gravity,顾名思义就是视频播放时的拉伸方式,默认是AVLayerVideoGravityResizeAspect
        // AVLayerVideoGravityResizeAspect 保持视频的宽高比并使播放内容自动适应播放窗口的大小。
        // AVLayerVideoGravityResizeAspectFill 和前者类似，但它是以播放内容填充而不是适应播放窗口的大小。最后一个值会拉伸播放内容以适应播放窗口.
        // 因为考虑到全屏显示以及设备自适应,这里我们采用fill填充
        self.preview.videoGravity = AVLayerVideoGravityResizeAspectFill;
        // 设置展示平台的frame
        self.preview.frame = CGRectMake(0, 0, ScreenW, ScreenH);
        // 因为 AVCaptureVideoPreviewLayer是继承CALayer,所以添加到当前view的layer层
        [self.view.layer insertSublayer:self.preview atIndex:0];
        
        [self.session startRunning];

    }else {
        
        [MM_AlertView MM_AlertWithTitle:@"" message:@"无法获取您的相机。\n请到【设置】->【仓储平台】->【相机】中打开，并允许仓储平台使用相机" withJH_AlertBlock:^(MM_AlertViewBtnType buttonIndexType) {
            if (buttonIndexType == MM_AlertViewBtnTypeEnsure) {
                
                if ([[UIApplication sharedApplication] canOpenURL:[NSURL  URLWithString:UIApplicationOpenSettingsURLString]]) {
                  [[UIApplication sharedApplication] openURL:[NSURL URLWithString: UIApplicationOpenSettingsURLString]];
                }
            }
        } cancelButtonTitle:@"取消" ensureButtonTitle:@"确定"];
        
    }
}

#pragma mark AVCaptureMetadataOutputObjectsDelegate

- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection {
    
    if ([metadataObjects count] >0){
        NSLog(@"========扫描后的url是:==============");
        NSMutableArray *muchArray = [NSMutableArray new];
        [metadataObjects enumerateObjectsUsingBlock:^(AVMetadataMachineReadableCodeObject *obj, NSUInteger idx, BOOL *stop) {
            if ([obj.type isEqualToString:AVMetadataObjectTypeQRCode] || [obj.type isEqualToString:AVMetadataObjectTypeAztecCode] || [obj.type isEqualToString:AVMetadataObjectTypeCode128Code]) {   //判断是否有数据，是否是二维码数据
                [muchArray addObject:obj];
            }
        }];
        [self.session stopRunning];
        //AVMetadataMachineReadableCodeObject是AVMetadataObject的具体子类定义的特性检测一维或二维条形码。
        
        if([muchArray count] == 1) {//扫描到一个二维码信息
            AVMetadataMachineReadableCodeObject * metadataObject = [muchArray objectAtIndex:0];
            NSString *stringValue = metadataObject.stringValue;
            
            
            if (!stringIsEmpty(stringValue)) {
                
                dispatch_async(dispatch_get_main_queue(), ^{
                    [self analyseResultAry:stringValue];
//                    self.reScanButton.hidden = NO;
                });
            }
            
        }else if([muchArray count] >1) {// 多个二维码信息 显示二维码定位页面  选择跳转
            
            self.qrCodesArray = [NSMutableArray new];
            
            [muchArray enumerateObjectsUsingBlock:^(AVMetadataMachineReadableCodeObject *result, NSUInteger idx, BOOL *stop) {
                
                NSMutableDictionary *dic = [NSMutableDictionary new];
                NSString *code = result.stringValue;
                [dic setObject:code forKey:@"code"];
                
                NSLog(@"2222 扫描后的url是:%@",code);
                
                // 标注多个二维码
                CGRect frame = [self makeFrameWithCodeObject:result Index:self.qrCodesArray.count];
                NSString *frameStr = NSStringFromCGRect(frame);
                [dic setObject:frameStr forKey:@"frame"];
                [self.qrCodesArray addObject:dic];//记录下标注的数组，下次扫码移除前面的标注
            }];
            
            dispatch_async(dispatch_get_main_queue(), ^{
//                self.reScanButton.hidden = NO;
            });
        }
    }
}
//选择多个二维码中的一个
- (void)handleBtnAction:(UIButton *)sender {
    NSInteger index = sender.tag - 1000;
    if (index < self.qrCodesArray.count) {
        NSDictionary *dic = self.qrCodesArray[index];
        if([dic.allKeys containsObject:@"code"]){
            NSString *urlString = [dic objectForKey:@"code"]?[dic objectForKey:@"code"]:@"";
            NSLog(@"2222 扫描后的url是: 选中 %@",urlString);
            if(!stringIsEmpty(urlString)) {
                
                [self analyseResultAry:urlString];
            }
        }
    }
}

/*
 AVMetadataMachineReadableCodeObject，输出的点位坐标是其在原始数据流上的坐标，与屏幕视图坐标不一样，（坐标系，值都会有差别）
 将坐标值转为屏幕显示的图像视图（self.videoPreviewLayer）上的坐标值
 */
-(CGRect)makeFrameWithCodeObject:(AVMetadataMachineReadableCodeObject *)objc Index:(NSInteger)index
{
    //将二维码坐标转化为扫码控件输出视图上的坐标
    //     CGSize isize = CGSizeMake(720.0, 1280.0); // 尺寸可以考虑不要写死,当前设置的是captureSession.sessionPreset = AVCaptureSessionPreset1280x720;
    CGSize isize = self.view.frame.size; //扫码控件的输出尺寸，
    float Wout = 0.00;
    float Hout = 0.00;
    BOOL wMore = YES;
    /*取分辨率与输出的layer尺寸差，
     此处以AVLayerVideoGravityResizeAspectFill填充方式为例，判断扫描的范围更宽还是更长，并计算出超出部分的尺寸，后续计算减去这部分。
     如果是其它填充方式，计算方式不一样（比如AVLayerVideoGravityResizeAspect，则计算计算留白的尺寸，并后续补足这部分）
     */
    if (isize.width/isize.height > self.view.bounds.size.width/self.view.bounds.size.height) {
        //当更宽时，计算扫描的坐标x为0 的点比输出视图的0点差多少（输出视图为全屏时，即屏幕外有多少）
        wMore = YES;
        Wout = (isize.width/isize.height)* self.view.bounds.size.height;
        Wout = Wout - self.view.bounds.size.width;
        Wout = Wout/2;
    }else{
        // 当更长时，计算y轴超出多少。
        wMore = NO;
        Hout = (isize.height/isize.width)* self.view.bounds.size.width;
        Hout = Hout  - self.view.bounds.size.height;
        Hout = Hout/2;
    }
    
    CGPoint point1 = CGPointZero;
    CGPoint point2 = CGPointZero;
    CGPoint point3 = CGPointZero;
    CGPoint point4 = CGPointZero;
    /*
     源坐标系下frame和角点，都是比例值，即源视频流尺寸下的百分比值。
     例子：frame ：(x = 0.26720550656318665, y = 0.0014114481164142489), size = (width = 0.16406852006912231, height = 0.29584407806396484))
     objc.corners：{0.26823519751360592, 0.29203594744002659}
     {0.4312740177700658, 0.29725551905635411}
     {0.4294213439632073, 0.012761536345436197}
     {0.26720551457151021, 0.0014114481640513654}
     */
    CGRect frame = objc.bounds;//在源坐标系的frame，
    NSArray *array = objc.corners;//源坐标系下二维码的角点
    CGPoint P = frame.origin;
    CGSize S = frame.size;
    
    //获取点
    for (int n = 0; n< array.count; n++) {
        
        CGPoint point = CGPointZero;
        CFDictionaryRef dict = (__bridge CFDictionaryRef)(array[n]);
        CGPointMakeWithDictionaryRepresentation(dict, &point);
//        NSLog(@"二维码角点%@",NSStringFromCGPoint(point));
        //交换xy轴
        point.x = point.y +  point.x;
        point.y = point.x - point.y;
        point.x = point.x - point.y;
        //x轴反转
        point.x = (1-point.x);
        //point乘以比列。减去尺寸差，
        if (wMore) {
            point.x = (point.x * (isize.width/isize.height)* self.view.bounds.size.height) - Wout;
            point.y = self.view.bounds.size.height *(point.y);
        }else{
            point.x = self.view.bounds.size.width *(point.x);
            point.y = (point.y) * (isize.height/isize.width)* self.view.bounds.size.width - Hout;
        }
        if (n == 0) {
            point1 = point;
        }
        if (n == 1) {
            point2 = point;
        }
        if (n == 2) {
            point3 = point;
        }
        if (n == 3) {
            point4 = point;
        }
    }
    //通过获取最小和最大的X，Y值，二维码在视图上的frame（前面得到的点不一定是正方形的二维码，也可能是菱形的或者有一定旋转角度的）
    float minX = point1.x;
    minX = minX>point2.x?point2.x:minX;
    minX = minX>point3.x?point3.x:minX;
    minX = minX>point4.x?point4.x:minX;
    
    float minY = point1.y;
    minY = minY>point2.y?point2.y:minY;
    minY = minY>point3.y?point3.y:minY;
    minY = minY>point4.y?point4.y:minY;
    P.x = minX;
    P.y = minY;
    
    float maxX = point1.x;
    maxX = maxX<point2.x?point2.x:maxX;
    maxX = maxX<point3.x?point3.x:maxX;
    maxX = maxX<point4.x?point4.x:maxX;
    
    float maxY = point1.y;
    maxY = maxY<point2.y?point2.y:maxY;
    maxY = maxY<point3.y?point3.y:maxY;
    maxY = maxY<point4.y?point4.y:maxY;
    
    S.width = maxX - minX;
    S.height = maxY - minY;
    
    //y轴坐标方向调整
    CGRect QRFrame = CGRectMake(P.x , P.y  , 50, 50);
    
    UIButton *tempButton = [UIButton buttonWithType:UIButtonTypeCustom];//多个二维码添加选择btn
    tempButton.backgroundColor = [UIColor blueColor];
    tempButton.frame = QRFrame;
    tempButton.layer.cornerRadius = 25;
    tempButton.layer.masksToBounds = YES;
    [self.view addSubview:tempButton];
    tempButton.tag = 1000 + index;
    [tempButton addTarget:self action:@selector(handleBtnAction:) forControlEvents:UIControlEventTouchUpInside];
    [self.qrCodesButtonArray addObject:tempButton];
    
    return QRFrame;
}

-(void)analyseResultAry:(NSString *)resultAsString {
    WS(weakSelf);
    [[AFNetworkReachabilityManager sharedManager] startMonitoring];
    [[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
        [[AFNetworkReachabilityManager sharedManager] stopMonitoring];
        if (status >= 1) {
            // 有网络
            if (resultAsString.length) {
              //  结果干点啥~~~~😊😊😊😊😊😊😊😊😊
                
                MM_CodeResultListVC *listVC = [[MM_CodeResultListVC alloc] init];
                listVC.code_str = resultAsString;
                [self.navigationController pushViewController:listVC animated:YES];

            } else {
                NSLog(@"识别结果内容：为空");
//                [weakSelf stopRunning];
            }
        } else {
            NSLog(@"没有网络");
//            [weakSelf stopRunning];
        }
    }];
}

- (NSMutableArray *)qrCodesArray {
    
    if (_qrCodesArray == nil) {
        
        _qrCodesArray = [[NSMutableArray alloc] init];
    }
    return _qrCodesArray;
}

- (NSMutableArray *)qrCodesButtonArray {
    
    if (_qrCodesButtonArray == nil) {
        
        _qrCodesButtonArray = [[NSMutableArray alloc] init];
    }
    return _qrCodesButtonArray;
}

@end
